package dmir.wikipedia.addons;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 
 * 
 * TODO update to use JAVA 7's NAMED GROUPS
 * 
 * @author ivo
 *
 */
public class SpanishWikiCoordinatesUtil extends EnglishWikiCoordinatesUtil {
    
    public static final SpanishWikiCoordinatesUtil INSTANCE = new SpanishWikiCoordinatesUtil();

    private static final String degs = "\\u02DA\\u00B0\\u00BA"; // ring above, degree sign, masculine ordinal indicator
    private static final String mins = "\\u2032\\u0027\\u00B4"; // prime, apostrophe, acute accent
    private static final String secs = "\\u2033\\u0022\\u02DD"; // double prime, quotation mark, double acute accent
    private static final String decCoords = "[\\d\\.\\+\\-NEWS]+";
    private static final String hmsCoords = "([\\d\\.]+)[" + degs +"]([\\s\\u200A]*([\\d\\.]+)[" + mins + "]([\\s\\u200A]*([\\d\\.]+)[" + secs + "])?)?[\\s\\u200A]*([NEWS])";

    private static final Pattern latLngPattern = Pattern.compile(
            "\\|\\s*((latitude)|(longitude))\\s*=\\s*((" + hmsCoords + "\\sto\\s" + hmsCoords + ")|(" + hmsCoords + ")|(" + decCoords + "))", 
            Pattern.CASE_INSENSITIVE);
    
    private static final Pattern latDegreesPattern = Pattern.compile(
            "\\|\\s*(latd|lat_d|lat_degrees|lat_grados|lat_deg|lat_gra|lat_grad)\\s*=\\s*(\\-?[\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern latMinutesPattern = Pattern.compile(
            "\\|\\s*(latm|lat_m|lat_minutes|lat_minutos|lat_min)\\s*=\\s*([\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern latSecondsPattern = Pattern.compile(
            "\\|\\s*(lats|lat_s|lat_seconds|lat_segundos|lat_sec|lat_seg)\\s*=\\s*([\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern latDirectionPattern = Pattern.compile(
            "\\|\\s*(latns|lat_ns|lat_direction|lat_direcció|lat_direccio|latNS)\\s*=\\s*([NS])", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern lngDegreesPattern = Pattern.compile(
            "\\|\\s*(longd|long_d|long_degrees|long_grados|long_deg|long_gra|long_grad)\\s*=\\s*(\\-?[\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern lngMinutesPattern = Pattern.compile(
            "\\|\\s*(longm|long_m|long_minutes|long_minutos|long_min)\\s*=\\s*([\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern lngSecondsPattern = Pattern.compile(
            "\\|\\s*(longs|long_s|long_seconds|long_sec|long_segundos|long_seg)\\s*=\\s*([\\d\\.]+)", 
            Pattern.CASE_INSENSITIVE);
    private static final Pattern lngDirectionPattern = Pattern.compile(
            "\\|\\s*(longew|long_ew|long_direction|long_direcció|long_direccio|longEW)\\s*=\\s*([EW])", 
            Pattern.CASE_INSENSITIVE);
    
    private static final Pattern coordinatesPattern = Pattern.compile(
            "\\|\\s*(coordinates|coord|coor|coords|coordenadas)\\s*=\\s*(.+?)\\s+", 
            Pattern.CASE_INSENSITIVE);
    private static final String hmsCoordInTemplate = "(([\\d\\.]+)\\|([\\d\\.]+)(\\|([\\d\\.]+))?\\|(N|S|E|W))";
    private static final String hmsCoordsInTemplate = hmsCoordInTemplate + "\\|" + hmsCoordInTemplate;
    private static final String decCoordsInTemplate = "([\\-\\+\\d\\.]+)(\\|(N|S))?\\|([\\-\\+\\d\\.]+)(\\|(W|E))?";
    private static final Pattern coordTemplatePattern = Pattern.compile(
            "\\{\\{coord\\|(("+ hmsCoordsInTemplate +")|(" + decCoordsInTemplate + "))", 
            Pattern.CASE_INSENSITIVE);
    
    public Double[] getCoordinates(CharSequence infobox) {
        
        Double[] latLng = new Double[2];
        
        try {

            if (infobox == null || infobox.length() == 0) return latLng;

            // coor / coord / coords / coordinates
            Matcher m = coordinatesPattern.matcher(infobox);
            if (m.find()) { 
                return convertWikiCoordToText(m.group(2)); 
            }


            // latitude / longitude
            m = latLngPattern.matcher(infobox);
            while (m.find()) {

                int index;
                if (m.group(2) != null) { //latitude
                    index = 0;
                } else if (m.group(3) != null) { // longitude
                    index = 1;
                } else {
                    continue;
                }


                if (m.group(25) != null) { //decimal
                    latLng[index] = Double.parseDouble(EnglishWikiCoordinatesUtil.normalizeDirections( m.group(25) ));

                } else if (m.group(18) != null) { //hms

                    StringBuilder a = new StringBuilder();
                    a.append(m.group(19));
                    if (m.group(21) != null) a.append(':').append(m.group(21));
                    if (m.group(23) != null) a.append(':').append(m.group(23));
                    if (m.group(24) != null) a.append(m.group(24));

                    latLng[index] = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( a.toString() ));

                } else if (m.group(5) != null) { //interval

                    StringBuilder a = new StringBuilder();
                    StringBuilder b = new StringBuilder();

                    a.append(m.group(6));
                    if (m.group(8) != null) a.append(':').append(m.group(8));
                    if (m.group(10) != null) a.append(':').append(m.group(10));
                    if (m.group(11) != null) a.append(m.group(11));
                    Double aVal = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( a.toString() ));

                    b.append(m.group(12));
                    if (m.group(14) != null) b.append(':').append(m.group(14));
                    if (m.group(16) != null) b.append(':').append(m.group(16));
                    if (m.group(17) != null) b.append(m.group(17));
                    Double bVal = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( b.toString() ));

                    latLng[index] = (bVal+aVal)/2;
                }


                if (latLng[0] != null && latLng[1] != null) {
                    return latLng;
                }
            }

            StringBuilder a = new StringBuilder();
            // latd / lat_d / lat_degrees / lat_deg
            m = latDegreesPattern.matcher(infobox);
            if (m.find()) { a.append(m.group(2));}
            // latm / lat_m / lat_minutes / lat_min
            m = latMinutesPattern.matcher(infobox);
            if (m.find()) { a.append(':').append(m.group(2)); }

            m = latSecondsPattern.matcher(infobox);
            if (m.find()) { a.append(':').append(m.group(2));}

            m = latDirectionPattern.matcher(infobox);
            if (m.find()) { a.append(m.group(2));}

            if (a.length() > 0) latLng[0] = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( a.toString() ));

            a.setLength(0);
            m = lngDegreesPattern.matcher(infobox);
            if (m.find()) { a.append(m.group(2)); }

            m = lngMinutesPattern.matcher(infobox);
            if (m.find()) { a.append(':').append(m.group(2)); }

            m = lngSecondsPattern.matcher(infobox);
            if (m.find()) { a.append(':').append(m.group(2)); }

            m = lngDirectionPattern.matcher(infobox);
            if (m.find()) { a.append(m.group(2)); }

            if (a.length() > 0) latLng[1] = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( a.toString() ));

        } catch (Exception e) {
            //do nothing
        }
        
        return latLng;
    }


    /**
     * 
     * handles wiki coord templates {{coord ... }}
     * 
     * @param coordTemplate
     * @return
     */
    private Double[] convertWikiCoordToText(CharSequence coordTemplate) {
        
        Double[] result = new Double[2];
        
        Matcher m = coordTemplatePattern.matcher(coordTemplate);
        if (m.find()) {
            
            if (m.group(2) != null) {
                
                String lath = m.group(4);
                String latm = m.group(5);
                String lats = m.group(7);
                String latd = m.group(8);
                String lngh = m.group(10);
                String lngm = m.group(11);
                String lngs = m.group(13);
                String lngd = m.group(14);
                
                StringBuilder sb = new StringBuilder();
                sb.append(lath);
                sb.append(':').append(latm);
                if (lats != null) sb.append(':').append(lats);
                if (latd != null) sb.append(latd);
                result[0] = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( sb.toString() ));
                
                sb.setLength(0);
                sb.append(lngh);
                sb.append(':').append(lngm);
                if (lngs != null) sb.append(':').append(lngs);
                if (lngd != null) sb.append(lngd);
                result[1] = EnglishWikiCoordinatesUtil.deg2Dec( EnglishWikiCoordinatesUtil.normalizeDirections( sb.toString() ));
                
            } else if (m.group(15) != null) { 
                
                String lat = m.group(16);
                String latd = m.group(18);
                String lng = m.group(19);
                String lngd = m.group(21);
                
                if (latd != null) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(lat);
                    sb.append(latd);
                    lat = EnglishWikiCoordinatesUtil.normalizeDirections(sb.toString());
                }
                result[0] = Double.parseDouble(lat); 
                
                if (lngd != null) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(lng);
                    sb.append(lngd);
                    lng = EnglishWikiCoordinatesUtil.normalizeDirections(sb.toString());
                }
                result[1] = Double.parseDouble(lng);
            }
        }
        
        return result;
    }
    
}
